﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
//using System.Text.RegularExpressions;
using System.Timers;
using System.Threading;

using LuckyBingoRcon.Matches;
using LuckyBingoRcon.Messages;

namespace LuckyBingoRcon
{
    public class RConV2
    {
        private bool connected = false;                                         // Made a successful acknowlegment to the server?
        private bool superAdmin = false;                                        // Is logged in user a super admin?
        private string password = null;                                         // Entered password
        private bool checkTimeStamp = false;                                    // Does the current user want to check if the time stamp is stale?
        private bool setMapList = true;
        private int challengeCounter = 0;                                       // If this gets to 2, boot the current Super Admin
        
        private IPEndPoint hostEndpoint = null;                                 // Server Address and Port
        
        private List<Match> matches = new List<Match>();  // Map Rotation Lists    
        private List<Message> messages = new List<Message>();                   // Message Rotation Lists
        private ServerState serverState = new ServerState();                    // Server State
        private ServerState formServerState = new ServerState();                // Server State

        private Queue<Commands.Command> commandQueue = new Queue<Commands.Command>();   // Holds commands to be queued
        private System.Timers.Timer commandTimer = new System.Timers.Timer(2000);       // When fired, grabs next command to send to server

        private List<LuckyBingoRcon.Players.Player> players = new List<LuckyBingoRcon.Players.Player>();

        // Possible commands
        LuckyBingoRcon.Commands.TeamStatusCommand tsCommand = null;
        LuckyBingoRcon.Commands.ExcludeMapsCommand emCommand = null;
        LuckyBingoRcon.Commands.PlaylistCommand plCommand = null;
        LuckyBingoRcon.Commands.MessageCommand mCommand = null;
        LuckyBingoRcon.Commands.KickBanCommand kbCommand = null;
        LuckyBingoRcon.Commands.ServernameCommand snCommand = null;
        LuckyBingoRcon.Commands.SATimeStampCommand satsCommand = null;
        LuckyBingoRcon.Commands.DvardumpCommand dvdCommand = null;
        LuckyBingoRcon.Commands.CustomCommand customCommand = null;

        // Rotation Counters
        private int rotationCounter = 0;
        private int messageCounter = 0;

        // Map and Gametype Checkers
        private MapLookup mapChecker = new MapLookup();
        private GametypeLookup gameChecker = new GametypeLookup();

        private Dictionary<string, string> serverVariables = new Dictionary<string, string>();
        
        public RConV2()
        {
            commandTimer.Elapsed += new ElapsedEventHandler(CheckQueue);
        }

        #region Connect
        /// <summary>
        /// Starts this.Listen as well as checks for a connection to the server.
        /// Blocks the thread until the connection is confiremd.
        /// </summary>
        /// <param name="remoteIP"></param>
        /// <param name="remotePort"></param>
        /// <param name="password"></param>
        public bool Connect(string remoteIP, int remotePort, string password)
        {
            if (connected)
                return true;

            hostEndpoint = new IPEndPoint(IPAddress.Parse(remoteIP), remotePort);
            this.tsCommand = new LuckyBingoRcon.Commands.TeamStatusCommand();
            this.tsCommand.Password = password;
            this.password = password;
            this.tsCommand.MapLookup = this.mapChecker;
            this.tsCommand.OnResponse += new EventHandler<Commands.Events.TeamStatusCommandEventArgs>(ConnectHeartBeat);
            this.tsCommand.Submit(hostEndpoint);
            
            // If connected == true, enable tab control to do stuff in GUI
            if (connected)
            {
                this.password = password;
                this.commandTimer.Enabled = true;
                this.commandTimer.Start();
                RconForm.Console.WriteLine("Successfully connected to the server");
            }
            else
            {
                this.password = null;
                RconForm.Console.WriteLine("Could not connect to server with given credentials");
            }
            return connected;
        }

        public void ConnectHeartBeat(object sender, Commands.Events.TeamStatusCommandEventArgs e)
        {
            if (!e.RawResponse.Contains("Invalid Password"))
            {
                this.players = e.Players;
                if (e.CurrentMap != null && this.mapChecker.IsValidName(e.CurrentMap.MapName))
                {
                    this.serverState.CurrentMap = e.CurrentMap;
                    this.formServerState.CurrentMap = this.serverState.CurrentMap;
                }
                connected = true;
            }
            else
            {
                connected = false;
            }
        }

        public bool Connected
        {
            get { return connected; }
        }
        #endregion

        #region Reset
        /// <summary>
        /// Stops listening and resets all the connection details.
        /// </summary>
        public void Reset()
        {
            while (this.commandQueue.Count > 0)
                System.Threading.Thread.Sleep(100);            
            this.commandTimer.Enabled = false;
            this.commandTimer.Stop();
            connected = false;
            password = null;
            hostEndpoint = null;
            RconForm.Console.WriteLine("Reset connection to server");
        }
        #endregion

        #region Map Rotation I/O
        public bool saveMapRotationConfig(List<Match> config, string fileName)
        {
            string sequence = string.Empty;
            foreach (Match entry in config)
                sequence += entry.GameType.PlayListNum + " " + entry.Map.MapName + ";";
            try
            {
                File.WriteAllText(fileName, sequence);
                return true;
            }
            catch
            {
                return false;
            }
        }

        public List<Match> loadMapRotationConfig(string fileName)
        {
            string text = File.ReadAllText(fileName);
            //string[] options = Regex.Split(text, ";");
            string[] options = text.Split(new char[] {';'});
            string[] args;
            int playlistNum;
            List<Match> listBoxList = new List<Match>();
            foreach (string pmp in options)
            {
                if (pmp.Equals(string.Empty))
                    continue;
                //args = Regex.Split(pmp, " ");
                args = pmp.Split(new char[] {' '});                
                if (args.Length.Equals(2) && this.gameChecker.IsValidPlayList(args[0]) && this.mapChecker.IsValidName(args[1]))               // Something weird happened with gametype map pair, skip if invalid
                {
                    playlistNum = Convert.ToInt32(args[0]);
                    listBoxList.Add(new Match(new Map(args[1], this.mapChecker.GetPrettyName(args[1]), this.mapChecker.GetAbbreviation(args[1])), new GameType(playlistNum, this.gameChecker.GetPlayListName(playlistNum))));
                }
            }
            return listBoxList;
        }
        #endregion

        #region Message Rotation I/O
        public bool saveMessageRotationConfig(List<Message> config, string fileName)
        {
            string sequence = string.Empty;
            foreach (Message entry in config)
            {
                sequence += entry.ToString() + Environment.NewLine;
            }
            try
            {
                File.WriteAllText(fileName, sequence);
                return true;
            }
            catch
            {
                return false;
            }
        }

        public List<Message> loadMessageRotationConfig(string fileName)
        {
            string text = File.ReadAllText(fileName);
            //string[] options = Regex.Split(text, Environment.NewLine);
            string[] options = text.Split(new string[] {Environment.NewLine}, StringSplitOptions.None);
            string[] args;
            int dur;
            List<Message> listBoxList = new List<Message>();
            foreach (string entry in options)
            {
                if (entry.Equals(string.Empty))
                    continue;
                //args = Regex.Split(entry, ";");
                args = entry.Split(new char[] {';'});
                if (args.Length.Equals(4))
                {
                    if (!string.IsNullOrEmpty(args[0]) && int.TryParse(args[1], out dur) && dur > 0 && this.gameChecker.IsValidPlayList(args[2]) && this.mapChecker.IsValidName(args[3]))
                        listBoxList.Add(new Message(args[0], dur, args[2], args[3]));
                }
            }
            return listBoxList;
        }
        #endregion

        #region Set Match & Message Lists
        public void setGametypeMapLists(List<Match> matches, bool forceList)
        {
            this.serverState.PreviousMap = new Map();
            this.rotationCounter = 0;
            if (this.setMapList && forceList)
                this.matches = matches;
            else if (forceList)
                this.setMapList = true;
            else if (!forceList)
                this.setMapList = false;
            try
            {
                this.SetExcludeMaps(new string[] { "\" \"" }, true);
            }
            catch (SocketException)
            {
                this.SetExcludeMaps(new string[] { "\" \"" }, true);
                //RconForm.Console.WriteLine(se.Message);
            }
        }

        public void setMessageLists(List<Message> messages)
        {
            this.messages = messages;
            this.messageCounter = 0;
        }
        #endregion

        #region Server Mutators
        private void setNewGametypeMap()
        {
            this.formServerState.CurrentMap = this.serverState.CurrentMap;
            this.formServerState.GameType = this.serverState.GameType;
            this.formServerState.ServerName = this.serverState.ServerName;

            // Are there matches listed to rotate on?
            if (this.matches.Count > 0)
            {
                // Check to make sure counter isn't larger than maps and gametypes list (check both for consistency)
                if (this.rotationCounter >= this.matches.Count)
                    this.rotationCounter = 0;
                // Maybe we can get this to work, but the server doesn't like when you omit a gametype that is currently playing
                /*string desiredGametype = this.matches[this.rotationCounter].GameType.GameName;
                List<int> allowPlayLists = this.gameChecker.GetAllowablePlaylists(desiredGametype);
                StringBuilder omitGames = new StringBuilder();
                if (allowPlayLists.Count > 0 && allowPlayLists[0] <= 8)
                {
                    List<string> games = new List<string>();
                    games.Add("tdm");
                    games.Add("dm");
                    games.Add("ctf");
                    games.Add("sd");
                    games.Add("koth");
                    games.Add("dom");
                    games.Add("sab");
                    games.Add("dem"); 
                    games.RemoveAt(allowPlayLists[0] - 1);
                    foreach (string game in games)
                        omitGames.Append(game).Append(" ");
                }
                string[] args = new string[] { "\"" + omitGames + "\"" };*/
                string[] args = new string[] { "\"" + this.matches[this.rotationCounter].GameType.PlayListNum.ToString() + "\"" };
                SetPlaylist(args);

                System.Threading.Thread.Sleep(500);

                // Exclude Map Packet
                string desiredMap = this.matches[this.rotationCounter].Map.MapName;
                string omitList = "\"";
                foreach (string map in mapChecker.GetUglyNames())
                {
                    if (!map.Equals(desiredMap))
                        omitList += map + " ";
                }
                omitList += "\"";
                string[] arg = new string[] { omitList };
                SetExcludeMaps(arg, false);
                if (this.rotationCounter >= this.matches.Count)
                {
                    this.formServerState.NextGameType = this.matches[0].GameType.GameName;
                    this.formServerState.NextMap = this.matches[0].Map.PrettyName;
                }
                else
                {
                    this.formServerState.NextGameType = this.matches[this.rotationCounter].GameType.GameName;
                    this.formServerState.NextMap = this.matches[this.rotationCounter].Map.PrettyName;
                }
                this.rotationCounter++;
                this.SetMapSwitchedState(true);
            }
        }

        // This might go away, I thought I was going to set more items for the form
        public void SetMapSwitchedState(bool state)
        {
            this.formServerState.MapSwitched = state;
        }

        public int displayMessage()
        {
            int newElapse = 10;
            // Check to make sure we have the proper server state information before we get too deep in this
            if (this.formServerState.GameType == null || this.formServerState.GameType.PlayListNum.Equals(-1) || this.formServerState.CurrentMap == null || this.formServerState.CurrentMap.MapName == null)
                return newElapse;
            // Are there messages to rotate on?
            if (this.messages.Count > 0)
            {
                if (this.messageCounter >= this.messages.Count)                 // Check if counter is larger than message size
                    this.messageCounter = 0;
                // Get current gametype and see if we have a message that is appropriate for it
                bool display = false;
                List<int> gameList = new List<int>();
                for (int index = this.messageCounter; index < this.messages.Count; index++)              // Each List<int> in visiblePlaylists can have multiple playlist numbers, i.e.: Team Deathmatch has playlist numbers 1, 9, and 17
                {
                    gameList = this.messages[index].GetVisiblePlayList;
                    if ((gameList.Contains(this.formServerState.GameType.PlayListNum) || gameList.Contains(0)) && (this.messages[index].GetMap.Equals("Any") || this.messages[index].GetMap.Equals(this.formServerState.CurrentMap.PrettyName)))
                    {
                        display = true;
                        this.messageCounter = index;
                        break;
                    }
                }
                if (!display)
                {
                    for (int index = 0; index < this.messageCounter; index++)
                    {
                        gameList = this.messages[index].GetVisiblePlayList;
                        if ((gameList.Contains(this.formServerState.GameType.PlayListNum) || gameList.Contains(0)) && (this.messages[index].GetMap.Equals("Any") || this.messages[index].GetMap.Equals(this.formServerState.CurrentMap.PrettyName)))
                        {
                            display = true;
                            this.messageCounter = index;
                            break;
                        }
                    }
                }
                if (display)
                {
                    newElapse = this.messages[this.messageCounter].GetDuration;
                    SetSayMessage(this.messages[this.messageCounter].GetMessage);
                    this.messageCounter++;
                }
            }
            return newElapse;
        }
        #endregion

        #region Accessors
        public List<Message> GetMessages()
        {
            return this.messages;
        }

        public List<Match> GetMatches()
        {
            return this.matches;
        }
        #endregion

        #region Queue
        private void CheckQueue(object source, ElapsedEventArgs e)
        {
            if (this.commandQueue.Count > 0)
            {
                try
                {
                    Commands.Command cmd = this.commandQueue.Dequeue();
                    if (cmd != null)
                    {
                        try
                        {
                            cmd.Submit(this.hostEndpoint);
                        }
                        catch (SocketException)
                        {
                            try
                            {
                                cmd.Cancel();
                                cmd.Submit(this.hostEndpoint);
                            }
                            catch (SocketException) { }
                        }
                    }
                }
                catch { }
            }
        }
        #endregion

        #region Team Status Command and Players ***
        // No SetTeamStatus(), read-only
        public void GetTeamStatus()
        {
            this.tsCommand = new LuckyBingoRcon.Commands.TeamStatusCommand();
            this.tsCommand.Password = this.password;
            this.tsCommand.MapLookup = this.mapChecker;
            this.tsCommand.OnResponse += new EventHandler<Commands.Events.TeamStatusCommandEventArgs>(ReceiveTeamStatus);
            this.commandQueue.Enqueue(this.tsCommand);
        }

        public void ReceiveTeamStatus(object sender, Commands.Events.TeamStatusCommandEventArgs e)
        {
            this.players = e.Players;
            if (e.CurrentMap != null && this.mapChecker.IsValidName(e.CurrentMap.MapName))
            {
                this.serverState.CurrentMap = e.CurrentMap;
                this.formServerState.CurrentMap = this.serverState.CurrentMap;
                if (this.serverState.PreviousMap == null || this.serverState.PreviousMap.MapName == null || !this.serverState.PreviousMap.MapName.Equals(e.CurrentMap.MapName))
                {
                    if (this.setMapList)
                    {
                        this.serverState.PreviousMap = this.serverState.CurrentMap;
                        setNewGametypeMap();
                    }
                }
            }
        }

        public List<LuckyBingoRcon.Players.Player> GetPlayers()
        {
            return this.players;
        }
        #endregion

        #region Placeholder Response
        private void EmptyResponse(object sender, EventArgs e)
        {
            // A placeholder so OnResponse doesn't throw a NullException
        }
        #endregion

        #region Server State and Exclude Map Command ***
        public ServerState GetServerState()
        {
            return this.formServerState;
        }

        private void SendExcludeMapsThreaded()
        {
            try
            {
                this.emCommand.Submit(this.hostEndpoint);
            }
            catch (SocketException)                                     // Need to try this again, important packet
            {
                try
                {
                    this.emCommand.Cancel();
                    this.emCommand.Submit(this.hostEndpoint);
                }
                catch (SocketException) { }
            }
        }

        private void SendMapRotationThreaded()
        {
            try
            {
                this.customCommand.Submit(this.hostEndpoint);
            }
            catch (SocketException)                                     // Need to try this again, important packet
            {
                try
                {
                    this.customCommand.Cancel();
                    this.customCommand.Submit(this.hostEndpoint);
                }
                catch (SocketException) { }
            }
        }

        public void SetExcludeMaps(string[] args, bool forceSend)
        {
            this.emCommand = new LuckyBingoRcon.Commands.ExcludeMapsCommand(true);
            this.emCommand.Password = this.password;
            this.emCommand.MapLookup = this.mapChecker;
            this.emCommand.OnResponse += new EventHandler<Commands.Events.ExcludeMapsCommandEventArgs>(EmptyResponse);
            this.emCommand.Args = args;
            if (!forceSend)
                this.commandQueue.Enqueue(this.emCommand);
            else
            {
                Thread t = new Thread(new ThreadStart(SendExcludeMapsThreaded));
                t.Start();
                //this.emCommand.Submit(this.hostEndpoint);
                this.customCommand = new LuckyBingoRcon.Commands.CustomCommand("sv_mapRotation gametype ");
                this.customCommand.Password = this.password;
                this.customCommand.OnResponse += new EventHandler<Commands.Events.CustomCommandEventArgs>(EmptyResponse);
                Dictionary<string, List<int>> playLists = this.gameChecker.GetPlayListConversions();
                string gametypeAbbrev = string.Empty;
                foreach(KeyValuePair<string, List<int>> kvp in playLists)
                {
                    if(kvp.Value.Contains(this.serverState.GameType.PlayListNum))
                    {
                        gametypeAbbrev = kvp.Key;
                        break;
                    }
                }
                string[] games = this.mapChecker.GetUglyNames();
                List<string> clearMapArgs = new List<string>();
                clearMapArgs.Add(gametypeAbbrev);
                clearMapArgs.Add("map");
                foreach (string game in games)
                    clearMapArgs.Add(game);
                // clearMapArgs.Add("\"");
                this.customCommand.Args = clearMapArgs.ToArray();
                Thread t1 = new Thread(new ThreadStart(SendMapRotationThreaded));
                t1.Start();
                /*try
                {
                    this.customCommand.Submit(this.hostEndpoint);
                }
                catch (SocketException)                                     // Need to try this again, important packet
                {
                    this.customCommand.Cancel();
                    this.customCommand.Submit(this.hostEndpoint);
                }*/
            }
        }

        public void GetExcludeMaps()
        {
            this.emCommand = new LuckyBingoRcon.Commands.ExcludeMapsCommand(false);
            this.emCommand.Password = this.password;
            this.emCommand.MapLookup = this.mapChecker;
            this.emCommand.Args = null;
            this.emCommand.OnResponse += new EventHandler<Commands.Events.ExcludeMapsCommandEventArgs>(CheckExcludeList);
            this.commandQueue.Enqueue(this.emCommand);
        }

        private void CheckExcludeList(object sender, Commands.Events.ExcludeMapsCommandEventArgs e)
        {
            if(this.formServerState != null && this.formServerState.NextMap != null && e.MapNames.Contains(this.mapChecker.GetUglyName(this.formServerState.NextMap)))
            {
                string desiredMap = this.mapChecker.GetUglyName(this.formServerState.NextMap);
                string omitList = "\"";
                foreach (string map in mapChecker.GetUglyNames())
                {
                    if (!map.Equals(desiredMap))
                        omitList += map + " ";
                }
                omitList += "\"";
                string[] arg = new string[] { omitList };
                SetExcludeMaps(arg, false);
            }
        }
        #endregion

        #region Playlist Command ***
        public void GetPlaylist(bool updateServer)
        {
            this.plCommand = new LuckyBingoRcon.Commands.PlaylistCommand(false);
            this.plCommand.Password = this.password;
            this.plCommand.GametypeLookup = this.gameChecker;
            this.plCommand.Args = null;                 // Just in case
            if (updateServer)
                this.plCommand.OnResponse += new EventHandler<Commands.Events.PlaylistCommandEventArgs>(ReceivePlaylist);
            else
                this.plCommand.OnResponse += new EventHandler<Commands.Events.PlaylistCommandEventArgs>(ReceivePlaylistQuery);
            this.commandQueue.Enqueue(this.plCommand);
        }

        public void SetPlaylist(string[] args)
        {
            this.plCommand = new LuckyBingoRcon.Commands.PlaylistCommand(true);
            this.plCommand.Password = this.password;
            this.plCommand.Args = args;
            this.plCommand.OnResponse += new EventHandler<Commands.Events.PlaylistCommandEventArgs>(EmptyResponse);
            this.commandQueue.Enqueue(this.plCommand);
        }

        public void ReceivePlaylist(object sender, Commands.Events.PlaylistCommandEventArgs e)
        {
            if (!e.PlayList.Equals(-1) && this.gameChecker.IsValidPlayList(e.PlayList.ToString()))
            {
                this.serverState.GameType = new GameType(e.PlayList, this.gameChecker.GetPlayListName(e.PlayList));
                string gameName = this.gameChecker.GetPlayListName(e.PlayList);
                if (!this.superAdmin)
                {
                    this.formServerState.NextGameType = gameName;
                }
                this.serverState.GameType = new GameType(e.PlayList, gameName);
            }
        }

        public void ReceivePlaylistQuery(object sender, Commands.Events.PlaylistCommandEventArgs e)
        {
            if (!e.PlayList.Equals(-1) && this.gameChecker.IsValidPlayList(e.PlayList.ToString()))
            {
                this.serverState.GameType = new GameType(e.PlayList, this.gameChecker.GetPlayListName(e.PlayList));
                if (!this.superAdmin)
                {
                    this.formServerState.NextGameType = this.gameChecker.GetPlayListName(e.PlayList);
                }
            }
        }
        #endregion

        #region Message Commands ***
        private void SendMessageThreaded()
        {
            try
            {
                this.mCommand.Submit(this.hostEndpoint);
            }
            catch (SocketException)                                     // Try this again
            {
                try
                {
                    this.mCommand.Cancel();
                    this.mCommand.Submit(this.hostEndpoint);
                }
                catch (SocketException) { }
            }
        }

        public void SetSayMessage(string message)
        {
            this.mCommand = new LuckyBingoRcon.Commands.MessageCommand(true);
            this.mCommand.Password = this.password;
            this.mCommand.OnResponse += new EventHandler<Commands.Events.MessageCommandEventArgs>(EmptyResponse);
            this.mCommand.Args = new string[] { message };
            Thread t = new Thread(new ThreadStart(SendMessageThreaded));
            t.Start();
            /*try
            {
                this.mCommand.Submit(this.hostEndpoint);
            }
            catch (SocketException)
            {}*/
        }

        public void SetTellMessage(int player, string message)
        {
            this.mCommand = new LuckyBingoRcon.Commands.MessageCommand(false);
            this.mCommand.Password = this.password;
            this.mCommand.OnResponse += new EventHandler<Commands.Events.MessageCommandEventArgs>(EmptyResponse);
            this.mCommand.Args = new string[] { player.ToString() + " \"" + message + "\"" };
            Thread t = new Thread(new ThreadStart(SendMessageThreaded));
            t.Start();
            /*try
            {
                this.mCommand.Submit(this.hostEndpoint);
            }
            catch (SocketException)
            {}*/
        }
        #endregion

        #region Kick and Ban Commands ***
        private void SendPlayerCommandThreaded()
        {
            try
            {
                this.kbCommand.Submit(this.hostEndpoint);
            }
            catch (SocketException)                                     // Try this again
            {
                try
                {
                    this.kbCommand.Cancel();
                    this.kbCommand.Submit(this.hostEndpoint);
                }
                catch (SocketException) { }
            }
        }

        // <password> clientkick <slot> ": <Message>"
        public void KickClient(int player, string message)
        {
            this.kbCommand = new LuckyBingoRcon.Commands.KickBanCommand(1);
            this.kbCommand.Password = this.password;
            this.kbCommand.OnResponse += new EventHandler<Commands.Events.KickBanCommandEventArgs>(EmptyResponse);
            this.kbCommand.Args = new string[] { player.ToString() + " \"" + message + "\"" };
            Thread t = new Thread(new ThreadStart(SendPlayerCommandThreaded));
            t.Start();
            /*try
            {
                this.kbCommand.Submit(this.hostEndpoint);
            }
            catch (SocketException)
            {
                this.kbCommand.Submit(this.hostEndpoint);
            }*/
        }

        // <password> tempbanclient <slot> <duration> ": <Message>"
        public void TempBanClient(int player, int duration, string message)
        {
            if (string.IsNullOrEmpty(message))
                message = "Temp Banned for " + duration.ToString() + " minutes";
            this.kbCommand = new LuckyBingoRcon.Commands.KickBanCommand(2);
            this.kbCommand.Password = this.password;
            this.kbCommand.OnResponse += new EventHandler<Commands.Events.KickBanCommandEventArgs>(EmptyResponse);
            this.kbCommand.Args = new string[] { player.ToString() + " " + duration.ToString() + " \"" + message + "\"" };
            Thread t = new Thread(new ThreadStart(SendPlayerCommandThreaded));
            t.Start();
            /*try
            {
                this.kbCommand.Submit(this.hostEndpoint);
            }
            catch (SocketException)
            {}*/
        }

        // <password> banclient <slot> ": <Message>"
        public void BanClient(int player, string message)
        {
            this.kbCommand = new LuckyBingoRcon.Commands.KickBanCommand(3);
            this.kbCommand.Password = this.password;
            this.kbCommand.OnResponse += new EventHandler<Commands.Events.KickBanCommandEventArgs>(EmptyResponse);
            this.kbCommand.Args = new string[] { "\"" + player.ToString() + "\"" };
            Thread t = new Thread(new ThreadStart(SendPlayerCommandThreaded));
            t.Start();
            /*try
            {
                this.kbCommand.Submit(this.hostEndpoint);
            }
            catch (SocketException)
            {}*/
        }
        #endregion

        #region Server Name Command ***
        public void GetServerName()
        {
            this.snCommand = new LuckyBingoRcon.Commands.ServernameCommand();
            this.snCommand.Password = this.password;
            this.snCommand.OnResponse += new EventHandler<Commands.Events.ServernameCommandEventArgs>(ReceiveServerName);
            this.commandQueue.Enqueue(this.snCommand);
        }

        public void ReceiveServerName(object sender, Commands.Events.ServernameCommandEventArgs e)
        {
            if (e.ServerName != null && !e.ServerName.Equals(this.serverState.ServerName))
            {
                this.serverState.ServerName = e.ServerName;
                this.formServerState.ServerName = e.ServerName;
            }
        }
        #endregion

        #region Server Admin Command ***
        public void GetServerAdmins()
        {
            if (!this.superAdmin)
            {
                this.satsCommand = new LuckyBingoRcon.Commands.SATimeStampCommand(false);
                this.satsCommand.Password = this.password;
                this.satsCommand.OnResponse += new EventHandler<Commands.Events.SATimeStampCommandEventArgs>(ReceiveTimeStamp);
                this.commandQueue.Enqueue(this.satsCommand);
            }
            else
            {
                string timeStamp = Convert.ToInt32(DateTime.UtcNow.TimeOfDay.TotalSeconds).ToString();
                string curRotation = string.Empty;
                for (int index = this.rotationCounter; index < this.matches.Count; index++)
                {
                    curRotation += this.matches[index].Map.Abbreviation + this.matches[index].GameType.PlayListNum;
                }
                for (int index = 0; index < this.rotationCounter; index++)
                {
                    curRotation += this.matches[index].Map.Abbreviation + this.matches[index].GameType.PlayListNum;
                }
                SetTimeStamp(new string[] { "\"" + timeStamp, this.rotationCounter.ToString(), curRotation + "\"" });
            }
        }

        public void SetTimeStamp(string[] args)
        {
            this.satsCommand = new LuckyBingoRcon.Commands.SATimeStampCommand(true);
            this.satsCommand.Password = this.password;
            this.satsCommand.OnResponse += new EventHandler<Commands.Events.SATimeStampCommandEventArgs>(EmptyResponse);
            this.satsCommand.Args = args;
            this.commandQueue.Enqueue(this.satsCommand);
        }

        public void ReceiveTimeStamp(object sender, Commands.Events.SATimeStampCommandEventArgs e)
        {
            if (e.timeStamp != null)
            {
                string response = e.timeStamp.Replace("\"", "");
                //string[] args = Regex.Split(response, " ");
                string[] args = response.Split(new char[] {' '});
                if (this.checkTimeStamp)
                {
                    string timeStamp = args[0];
                    string rotationList = string.Empty;
                    string newRotationList = string.Empty;
                    int checkIn = -1000;
                    if (!String.IsNullOrEmpty(timeStamp))
                        int.TryParse(timeStamp, out checkIn);
                    int curTime = Convert.ToInt32(DateTime.UtcNow.TimeOfDay.TotalSeconds);
                    if (Math.Abs(checkIn - curTime) > 120 && Math.Abs(checkIn - curTime) < 86280)        // Time stamp is stale
                    {
                        this.challengeCounter++;
                        if (this.challengeCounter >= 2)
                        {
                            RconForm.Console.WriteLine("Previous server admin lost connection with server, grabbing match rotation");
                            this.challengeCounter = 0;
                            if (args.Length.Equals(3) && int.TryParse(args[1], out this.rotationCounter))
                            {
                                rotationList = args[2];
                                newRotationList = rotationList;
                                this.matches.Clear();
                                try
                                {
                                    string abbrev;
                                    string uglyName;
                                    string prettyName;
                                    int playList;
                                    string gameType;
                                    while (!string.IsNullOrEmpty(rotationList))
                                    {
                                        if (rotationList.Length >= 4)
                                        {
                                            abbrev = rotationList.Substring(0, 2);
                                            uglyName = this.mapChecker.GetUglyFromAbbrev(abbrev);
                                            prettyName = this.mapChecker.GetPrettyName(uglyName);
                                            if (int.TryParse(rotationList.Substring(2, 2), out playList))
                                            {
                                                gameType = this.gameChecker.GetPlayListName(playList);
                                                if (abbrev != null && uglyName != null && prettyName != null && !playList.Equals(-1) && gameType != null)
                                                    this.matches.Add(new Match(new Map(uglyName, prettyName, abbrev), new GameType(playList, gameType)));
                                            }
                                            try
                                            {
                                                rotationList = rotationList.Substring(4);
                                            }
                                            catch
                                            {
                                                RconForm.Console.WriteLine("Could not download match rotation - incomplete item in list");
                                                break;
                                            }
                                        }
                                        else
                                        {
                                            RconForm.Console.WriteLine("Could not download match rotation - incomplete item in list");
                                            break;
                                        }
                                    }
                                    this.setMapList = false;
                                    if (this.matches.Count > 0 && this.formServerState.CurrentMap.MapName.Equals(this.matches[0].Map.MapName))
                                    {
                                        newRotationList += newRotationList.Substring(0, 4);
                                        newRotationList = newRotationList.Remove(0, 4);
                                        Match firstMatch = this.matches[0] as Match;
                                        this.matches.Add(firstMatch);
                                        this.matches.RemoveAt(0);
                                    }
                                }
                                catch
                                {
                                    RconForm.Console.WriteLine("Could not parse match rotation");
                                }
                                rotationList = args[2];
                            }
                            SetTimeStamp(new string[] { "\"" + Convert.ToInt32(DateTime.UtcNow.TimeOfDay.TotalSeconds).ToString(), this.rotationCounter.ToString(), newRotationList + "\"" });
                            this.superAdmin = true;
                        }
                    }
                    else
                        this.challengeCounter = 0;
                }
                else
                {
                    if (args.Length.Equals(3))
                    {
                        try
                        {
                            int playList = -1;
                            string lastMatch = args[2].Substring(args[2].Length - 2);
                            if (int.TryParse(lastMatch, out playList))
                                this.formServerState.GameType = new GameType(playList, this.gameChecker.GetPlayListName(playList));
                        }
                        catch { }
                    }
                }
            }
        }

        public bool IsSuperAdmin()
        {
            return this.superAdmin;
        }

        public void SetTimeStampCheck(bool val)
        {
            this.checkTimeStamp = val;
        }
        #endregion

        #region Server Variables Command ***
        public void GetServerVariables()
        {
            this.dvdCommand = new LuckyBingoRcon.Commands.DvardumpCommand(false);
            this.dvdCommand.Password = this.password;
            this.dvdCommand.OnResponse += new EventHandler<Commands.Events.ServerVarsCommandEventArgs>(ReceiveServerVariables);
            this.commandQueue.Enqueue(this.dvdCommand);
        }

        public void SetServerVariable(string variableName, string value)
        {
            this.dvdCommand = new LuckyBingoRcon.Commands.DvardumpCommand(true);
            this.dvdCommand.Password = this.password;
            this.dvdCommand.OnResponse += new EventHandler<Commands.Events.ServerVarsCommandEventArgs>(EmptyResponse);
            string[] args = new string[] { variableName, value };
            this.dvdCommand.Args = args;
            this.commandQueue.Enqueue(this.dvdCommand);
        }

        public void ReceiveServerVariables(object sender, Commands.Events.ServerVarsCommandEventArgs e)
        {
            if (e.ServerVariables != null)
                this.serverVariables = e.ServerVariables;
        }

        public Dictionary<string, string> GetVariables()
        {
            return this.serverVariables;
        }
        #endregion

        #region Custom Commands from Console

		public EventHandler<Commands.Events.CustomCommandEventArgs> OnCusomCommandResponse;

        public void SendCustomCommand(object data)
        {
			try
			{
				string command = data as string;
				if (command == null)
					return;
				//this.consoleResponse = null;
				this.customCommand = new LuckyBingoRcon.Commands.CustomCommand(command);
				this.customCommand.Password = this.password;
				this.customCommand.OnResponse += new EventHandler<Commands.Events.CustomCommandEventArgs>(ReceiveCustomCommand);
				this.customCommand.Submit(this.hostEndpoint);
			}
			catch (SocketException)
			{
				// eat
			}
        }

        public void ReceiveCustomCommand(object sender, Commands.Events.CustomCommandEventArgs e)
        {
			if (OnCusomCommandResponse != null)
			{
				OnCusomCommandResponse(sender, e);
			}
        }
        #endregion
    }
}